package com.mall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.mall.common.util.PageInfo;
import com.mall.search.mapper.SkuSearchMapper;
import com.mall.search.model.SkuEs;
import com.mall.search.service.ISkuSearchService;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author Darren
 * @date 2022-08-22 15:22
 * Description：
 */
@Service
public class SkuSearchServiceImpl implements ISkuSearchService {
    @Autowired
    private SkuSearchMapper skuSearchMapper;

    @Override
    @SuppressWarnings("unchecked")
    public void addIndex(SkuEs skuEs) {
        // 将skuAttribute转换为Map添加到attrMap中
        if (StringUtils.isNotEmpty(skuEs.getSkuAttribute()))
            skuEs.setAttrMap(JSON.parseObject(skuEs.getSkuAttribute(), Map.class));
        skuSearchMapper.save(skuEs);
    }

    @Override
    public void delIndex(String id) {
        skuSearchMapper.deleteById(id);
    }

    @Override
    public Map<String, Object> search(Map<String, Object> searchMap) {
        // 构建搜索条件
        NativeSearchQueryBuilder queryBuilder = this.queryBuilder(searchMap);
        // 构建分组条件
        this.groupBuilder(queryBuilder, searchMap, "category", "categoryList", "categoryName", 100);
        this.groupBuilder(queryBuilder, searchMap, "brand", "brandList", "brandName", 100);
        this.groupBuilder(queryBuilder, searchMap, null, "attrmaps", "skuAttribute", 10000);
        // TODO: 高亮显示
        // 进行搜索
        AggregatedPage<SkuEs> page = (AggregatedPage<SkuEs>) skuSearchMapper.search(queryBuilder.build());
        // 获取结果集组装
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("list", page.getContent());
        // 分组结果集组装
        this.parseGroup(page.getAggregations(), resultMap);
        // 将属性信息合并成Map对象
        this.attrParse(resultMap);
        // 分页创建
        PageInfo pageInfo = new PageInfo();
        int current = searchMap.containsKey("current") ? Integer.parseInt(searchMap.get("current").toString()) : 1;
        int size = searchMap.containsKey("size") ? Integer.parseInt(searchMap.get("size").toString()) : 5;
        pageInfo.initPage(page.getTotalElements(), current, size);
        resultMap.put("pageInfo", pageInfo);
        return resultMap;
    }

    @Override
    @SuppressWarnings("unchecked")
    public void refreshIndex() {
//        List<Sku> list = skuService.list();
//        for (Sku sku : list) {
//            SkuEs skuEs = JSON.parseObject(JSON.toJSONString(sku), SkuEs.class);
//            if (StringUtils.isNotEmpty(skuEs.getSkuAttribute()))
//                skuEs.setAttrMap(JSON.parseObject(skuEs.getSkuAttribute(), Map.class));
//            skuSearchMapper.save(JSON.parseObject(JSON.toJSONString(sku), SkuEs.class));
//        }
    }

    /**
     * 搜索条件构建
     *
     * @param searchMap
     * @return
     */
    private NativeSearchQueryBuilder queryBuilder(Map<String, Object> searchMap) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 条件构建
        if (searchMap != null && searchMap.size() > 0) {
            // 关键词
            Object keywords = searchMap.get("keywords");
            if (Objects.nonNull(keywords))
                boolQueryBuilder.must(QueryBuilders.termQuery("name", keywords));
            // 分类
            Object category = searchMap.get("category");
            if (Objects.nonNull(category))
                boolQueryBuilder.must(QueryBuilders.termQuery("categoryName", category));
            // 品牌
            Object brand = searchMap.get("brand");
            if (Objects.nonNull(brand))
                boolQueryBuilder.must(QueryBuilders.termQuery("brandName", brand));
            // 价格
            Object price = searchMap.get("price");
            if (Objects.nonNull(price)) {
                String[] split = price.toString().replace("元", "").replace("以上", "").split("-");
                boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gt(Integer.valueOf(split[0])));
                if (split.length == 2) {
                    boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lte(Integer.valueOf(split[1])));
                }
            }
            // 属性查询
            for (Map.Entry<String, Object> entry : searchMap.entrySet()) {
                if (entry.getKey().startsWith("attr_")) {
                    String key = entry.getKey().replaceFirst("attr_", "");
                    boolQueryBuilder.must(QueryBuilders.termQuery("attrMap." + key + ".keyword", entry.getValue()));
                }
            }
            // 排序，因为没有涉及到数据的过滤，使用NativeSearchQueryBuilder
            Object sfield = searchMap.get("sfield");
            Object sm = searchMap.get("sm");
            if (Objects.nonNull(sfield) && Objects.nonNull(sm))
                queryBuilder.withSort(SortBuilders.fieldSort(sfield.toString()).order(SortOrder.valueOf(sm.toString().toUpperCase())));
        }
        // 分页构建
        int current = searchMap.containsKey("current") ? Integer.parseInt(searchMap.get("current").toString()) - 1 : 0;
        int size = searchMap.containsKey("size") ? Integer.parseInt(searchMap.get("size").toString()) : 5;
        queryBuilder.withPageable(PageRequest.of(current, size));
        return queryBuilder.withQuery(boolQueryBuilder);
    }

    /**
     * 分组查询条件构建
     * 如果用户没有输入分类条件，则需要将分类搜索出来，作为条件提供给用户
     *
     * @param queryBuilder
     * @param searchMap
     * @param mapKey
     * @param resultKey
     * @param esColumnName
     * @param size
     */
    private void groupBuilder(NativeSearchQueryBuilder queryBuilder, Map<String, Object> searchMap,
                              String mapKey, String resultKey, String esColumnName, int size) {
        if (mapKey == null || !searchMap.containsKey(mapKey)) {
            queryBuilder.addAggregation(
                    AggregationBuilders
                            // 返回结果集的key
                            .terms(resultKey)
                            // ES中的数据名
                            .field(esColumnName)
                            // 最大条数
                            .size(size)
            );
        }
    }

    /**
     * 分组结果解析
     *
     * @param aggregations
     * @param resultMap
     */
    private void parseGroup(Aggregations aggregations, Map<String, Object> resultMap) {
        if (Objects.isNull(aggregations)) return;
        for (Aggregation aggregation : aggregations) {
            // 强转为ParsedStringTerms
            ParsedStringTerms terms = (ParsedStringTerms) aggregation;
            // 循环结果集
            List<String> values = new ArrayList<>();
            for (Terms.Bucket bucket : terms.getBuckets()) {
                values.add(bucket.getKeyAsString());
            }
            resultMap.put(terms.getName(), values);
        }
    }

    /**
     * 将属性信息合并成Map对象
     *
     * @param resultMap
     */
    private void attrParse(Map<String, Object> resultMap) {
        Object attrmaps = resultMap.get("attrmaps");
        if (Objects.isNull(attrmaps))
            return;
        List<String> attrList = (List<String>) attrmaps;
        Map<String, Set<Object>> attrMaps = new HashMap<>();
        for (String attr : attrList) {
            Map<String, String> attrMap = JSON.parseObject(attr, Map.class);
            for (Map.Entry<String, String> entry : attrMap.entrySet()) {
                Set<Object> set = attrMaps.get(entry.getKey());
                if (set == null) set = new HashSet<>();
                set.add(entry.getValue());
                attrMaps.put(entry.getKey(), set);
            }
        }
        resultMap.put("attrmaps", attrMaps);
    }
}
